#pragma once
#include "../3rd/cpp-httplib/httplib.h"
#include "../3rd/rapidjson/include/rapidjson/document.h"
#include "utils/dbutils.hpp"
#include "utils/transutils.hpp"

using namespace httplib;
using namespace rapidjson;

class DoneeFilter {
 public:
  static DoneeFilter *GetInstance() {
    static DoneeFilter filter{"", ""};
    return &filter;
  }
  std::string doneeId;
  std::string doneeName;
};

class Donee_ {
 public:
  std::string doneeId;
  std::string doneeName;
  std::string phoneNumber;
  std::string address;
  std::string email;

  template <typename Writer>
  void Serialize(Writer &writer) const {
    writer.StartObject();
    writer.String("doneeId");
    writer.String(doneeId.c_str());
    writer.String("doneeName");
    writer.String(doneeName.c_str());
    writer.String("phoneNumber");
    writer.String(phoneNumber.c_str());
    writer.String("address");
    writer.String(address.c_str());
    writer.String("email");
    writer.String(email.c_str());
    writer.EndObject();
  }
};

void Donee(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");

  Document doc;
  doc.Parse(req.body.c_str());
  auto currentPage = std::stoi(req.get_param_value("currentPage"));
  auto pageSize = std::stoi(req.get_param_value("pageSize"));
  auto offset = (currentPage - 1) * pageSize;

  auto sql = "select * from donees where doneeName like " +
             FuzzyString(DoneeFilter::GetInstance()->doneeName);

  if (!DoneeFilter::GetInstance()->doneeId.empty()) {
    sql += " and doneeId = " + Qstring(DoneeFilter::GetInstance()->doneeId);
  }
  sql += " limit " + std::to_string(offset) + ", " + std::to_string(pageSize) +
         ";";

  auto result = DBWrapper::GetInstance()->Select(sql);

  std::string json;
  json += "[";
  bool first = true;
  for (size_t i = 0; i < result->size(); ++i) {
    const auto &row = result->at(i);

    Donee_ donee{row[0], row[1], row[2], row[3], row[4]};
    if (!first) {
      json += ", ";
    }
    json += Object2Json(donee);
    first = false;
  }
  json += "]";

  res.status = 200;
  res.set_content(json, "application/json");
}

void DoneeTotal(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");

  DoneeFilter::GetInstance()->doneeId = "";
  DoneeFilter::GetInstance()->doneeName = "";

  auto sql = "select count(*) from donees;";
  auto result = DBWrapper::GetInstance()->Select(sql);
  hassert(result.get()->size() > 0, "SELECT no rows");
  res.status = 200;
  res.set_content("{\"totalRow\": " + result->begin()->at(0) + "}",
                  "application/json");
}

void DoneeAdd(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");

  Document doc;
  doc.Parse(req.body.c_str());
  auto doneeId = Qstring("NULL");
  auto doneeName = Qstring(doc["doneeName"].GetString());
  auto phoneNumber = Qstring(doc["phoneNumber"].GetString());
  auto address = Qstring(doc["address"].GetString());
  auto email = Qstring(doc["email"].GetString());

  if (DBWrapper::GetInstance()->Insert(
          "donees", {doneeId, doneeName, phoneNumber, address, email})) {
    res.status = 200;
  } else {
    res.status = 202;
  }
}

void DoneeDelete(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");

  auto doneeId = req.get_param_value("doneeId");

  auto sql = "delete from donees where doneeId=" + doneeId + ";";

  if (DBWrapper::GetInstance()->Excute(sql)) {
    res.status = 200;
  } else {
    res.status = 202;
  }
}

void DoneeEdit(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");

  Document doc;
  doc.Parse(req.body.c_str());
  auto doneeId = Qstring(doc["doneeId"].GetString());
  auto doneeName = Qstring(doc["doneeName"].GetString());
  auto phoneNumber = Qstring(doc["phoneNumber"].GetString());
  auto address = Qstring(doc["address"].GetString());
  auto email = Qstring(doc["email"].GetString());

  auto sql = "delete from donees where doneeId=" + doneeId + ";";

  if (DBWrapper::GetInstance()->Excute(sql) &&
      DBWrapper::GetInstance()->Insert(
          "donees", {doneeId, doneeName, phoneNumber, address, email})) {
    res.status = 200;
  } else {
    res.status = 202;
  }
}

void DoneeSearch(const Request &req, Response &res) {
  res.set_header("Access-Control-Allow-Origin", "*");
  DoneeFilter::GetInstance()->doneeId = req.get_param_value("doneeId");
  DoneeFilter::GetInstance()->doneeName = req.get_param_value("doneeName");

  auto sql = "select * from donees where doneeName like " +
             FuzzyString(DoneeFilter::GetInstance()->doneeName);

  if (!DoneeFilter::GetInstance()->doneeId.empty()) {
    sql += " and doneeId = " + Qstring(DoneeFilter::GetInstance()->doneeId);
  }
  sql += ";";
  auto result = DBWrapper::GetInstance()->Select(sql);
  res.set_content("{\"totalRow\": " + result->at(0).at(0) + "}",
                  "application/json");
  res.status = 200;
}